﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BeanboxSoftware.BeanMap;
using BeanboxSoftware.BeanMapTest;

namespace BeanMapTest.Unboxed
{
  [TestClass]
  public class MapKVTest
  {
    private Map<int, int> _map;

    [TestInitialize]
    public void SetUp()
    {
      _map = new Map<int, int>();
    }

    [TestMethod]
    public void SetGetRangeTest()
    {
      _map[1] = 2;
      var v = _map[1];

      Assert.AreEqual(2, v);
    }

    [TestMethod]
    public void ContainsKeyTest()
    {
      Assert.IsFalse(_map.ContainsKey(1));

      _map[1] = 2;

      Assert.IsTrue(_map.ContainsKey(1));
    }

    [TestMethod]
    public void DefaultBehaviorTest()
    {
      Assert.AreEqual(KeyNotFoundBehavior.ThrowKeyNotFoundException, _map.KeyNotFoundBehavior);
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1]; });
    }

    [TestMethod]
    public void DefaultValueTest()
    {
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1]; });
      Assert.IsFalse(_map.ContainsKey(1));
      _map.DefaultValue = -1;
      Assert.AreEqual(KeyNotFoundBehavior.DefaultValue, _map.KeyNotFoundBehavior);
      Assert.AreEqual(-1, _map[1]);
      Assert.IsFalse(_map.ContainsKey(1));
    }

    [TestMethod]
    public void DefaultGenerationTest()
    {
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1]; });
      Assert.IsFalse(_map.ContainsKey(1));
      _map.DefaultGeneration = k => k;
      Assert.AreEqual(KeyNotFoundBehavior.DefaultGeneration, _map.KeyNotFoundBehavior);
      Assert.AreEqual(1, _map[1]);
      Assert.IsTrue(_map.ContainsKey(1));
    }


    [TestMethod]
    public void CountTest()
    {
      Assert.AreEqual(0, _map.Count);
      _map[1] = 3;
      Assert.AreEqual(1, _map.Count);
      _map[2] = 4;
      Assert.AreEqual(2, _map.Count);
    }

    [TestMethod]
    public void TryGetTest()
    {
      int value;
      Assert.IsFalse(_map.TryGetValue(14, out value));
      _map[14] = 3;
      Assert.IsTrue(_map.TryGetValue(14, out value));
      Assert.AreEqual(3, value);
    }

    [TestMethod]
    public void InternalGetSetTest()
    {
      var dic = (IDictionary<int, int>)_map;

      Assert.IsFalse(_map.ContainsKey(1));
      dic[2] = 4;
      Assert.AreEqual(4, _map[2]);
      Assert.IsTrue(_map.ContainsKey(2));
    }

    [TestMethod]
    public void RemoveTest()
    {
      Assert.IsFalse(_map.Remove(1));
      _map[1] = 2;
      Assert.AreEqual(1, _map.Count);
      Assert.IsTrue(_map.ContainsKey(1));
      Assert.IsTrue(_map.Remove(1));
      Assert.AreEqual(0, _map.Count);
      Assert.IsFalse(_map.ContainsKey(1));
    }

    [TestMethod]
    public void BasicUseTest()
    {
      var map = new Map<int, string>();

      map[1] = "jun";
      map[2] = "cuib";

      Assert.IsFalse(map.ContainsKey(3), "ContainsKey(3)");

      map.DefaultValue = "mac'a";

      Assert.AreEqual("mac'a", map[3], "map[3] with default error value");
      Assert.IsFalse(map.ContainsKey(3), "ContainsKey(3) after default error value");

      map.DefaultGeneration = key => key.ToString();

      Assert.AreEqual("3", map[3], "map[3] with generator");
      Assert.IsTrue(map.ContainsKey(3), "ContainsKey(3) after generator");
    }

    [TestMethod]
    public void PropertiesTest()
    {
      var map = new Map<int, string>();
      map[0] = "mac'a";
      map[1] = "jun";

      Assert.AreEqual(2, map.Count, "count");

      Assert.IsTrue(map.Values.Contains("mac'a"), "values should contain mac'a");
      Assert.IsTrue(map.Values.Contains("jun"), "values should contain jun");
      Assert.IsFalse(map.Values.Contains("cuib"), "values should not contain cuib");

      Assert.IsTrue(map.Keys.Contains(0), "keys should contain 0");
      Assert.IsTrue(map.Keys.Contains(1), "keys should contain 1");
      Assert.IsFalse(map.Keys.Contains(2), "keys should contain 2");
    }

    [TestMethod]
    public void ListCtorTest()
    {
      var list = new List<int>();
      list.Add(1);
      list.Add(2);

      var map = new Map<string, int>(list, item => item.ToString());

      Assert.AreEqual(1, map["1"], "map[1]");
      Assert.AreEqual(2, map["2"], "map[2]");

      map.KeyNotFoundBehavior = KeyNotFoundBehavior.ThrowKeyNotFoundException;

      TestUtils.AssertExceptionThrown<KeyNotFoundException>(
        () =>
        {
          Assert.Fail(map["3"].ToString());
        }, "3 doesn't exist and should throw");
    }

    [TestMethod]
    public void KeyValueSetBaseTest()
    {
      var map = new Map<int, string>();
      map[3] = "three";

      var kvs = map.KeyValues;

      Assert.AreEqual("three", kvs.First().Value, "kv value 0");
      Assert.AreEqual(3, kvs.First().Key, "kv key 0");
    }

    [TestMethod]
    public void KeyNotFoundBehaviorTest()
    {
      var map = new Map<int, int>();
      map.DefaultGeneration = (key) => { return -1; };

      Assert.AreEqual(KeyNotFoundBehavior.DefaultGeneration, map.KeyNotFoundBehavior,
        "behavior after setting generator");

      map.DefaultValue = 41;

      Assert.AreEqual(41, map[414], "default value");
      Assert.IsFalse(map.ContainsKey(414), "default value should not create a key");
    }

    [TestMethod]
    public void ClearTest()
    {
      var map = new Map<int, int>();
      map[1] = 2;

      map.Clear();

      Assert.AreEqual(0, map.Count, "map count after clear");
    }

    [TestMethod]
    public void DictionaryLikeApiTest()
    {
      var dict = new Dictionary<int, string>()
      {
        {1, "one"},
        {2, "two"},
        {3, "three"},
      };

      var map = new Map<int, string>()
      {
        {1, "one"},
        {2, "two"},
        {3, "three"},
      };

      foreach (var key in map.Keys)
      {
        Assert.AreEqual(dict[key], map[key], "dict style init list for key: " + key.ToString());
      }

      TestUtils.AssertExceptionThrown<ArgumentException>(() => { map.Add(1, "other"); },
        "add should act like dict act");

      foreach (var it in map)
      {
        var kv = (KeyValuePair<int, string>)it;

        Assert.AreEqual(map[kv.Key], kv.Value, "kv key: " + kv.Key.ToString());
      }
    }

    [TestMethod]
    public void CopyCtor()
    {
      var map = new Map<int, string>();
      map[10] = "10";
      map[15] = "15";

      var copy = new Map<int, string>(map);

      Assert.AreEqual(map[10], copy[10], "10");
      Assert.AreEqual(map[15], copy[15], "15");

      copy[20] = "20";
      map.Remove(10);

      Assert.IsFalse(map.ContainsKey(20), "20");
      Assert.IsTrue(copy.ContainsKey(10), "10");
    }

    class Thing
    {
      public int Foo { get; set; }
      public int Bar { get; set; }
      public string Baz { get; set; }
    }

    [TestMethod]
    public void ListToMapTest()
    {
      var things = new List<Thing>
                 {
                     new Thing {Foo = 3, Bar = 4, Baz = "brown"},
                     new Thing {Foo = 6, Bar = 4, Baz = "jumps"}
                 };

      var thingMap = Map<int, string>.From(things, t => t.Foo, t => t.Baz);

      Assert.IsTrue(thingMap.ContainsKey(3));
      Assert.AreEqual("brown", thingMap[3]);

      thingMap.DefaultValue = string.Empty;
      Assert.AreEqual("brown", thingMap[3]);
      Assert.AreEqual(string.Empty, thingMap[2]);
    }

    class KeyTypeOne
    {
    }

    class KeyTypeTwo
    {
    }

    class ValueType
    {
    }

    [TestMethod]
    public void JaggedAccessTest()
    {
      var map = new Map<KeyTypeOne, KeyTypeTwo, ValueType>();

      var a = new KeyTypeOne();
      var i = new KeyTypeTwo();
      var v = new ValueType();

      var child = map[a];

      Assert.IsFalse(map.ContainsKey(a));
      Assert.IsNotNull(child);

      child[i] = v;

      Assert.IsTrue(map.ContainsKey(a));

      Assert.AreEqual(v, map[a][i]);
      Assert.AreEqual(v, map[a, i]);

      map.Clear();
      Assert.IsFalse(map.ContainsKey(a));
      Assert.IsFalse(map.ContainsKey(a, i));
      map[a][i] = v;
      Assert.AreEqual(v, map[a][i]);
      Assert.AreEqual(v, map[a, i]);
      Assert.AreEqual(v, child[i]);
    }

    [TestMethod]
    public void JaggedConfigTest()
    {
      var map = new Map<KeyTypeOne, KeyTypeTwo, ValueType>();

      var a = new KeyTypeOne();
      var i = new KeyTypeTwo();
      var v = new ValueType();
      var e = new ValueType();

      map.DefaultValue = e;

      var child = map[a];

      Assert.AreEqual(e, map[a][i]);
      Assert.AreEqual(e, map[a, i]);
      Assert.AreEqual(e, child[i]);
      Assert.IsFalse(map.ContainsKey(a, i));
      Assert.IsFalse(child.ContainsKey(i));

      map.DefaultGeneration = (k1, k2) => v;

      Assert.AreEqual(v, map[a][i]);

      Assert.IsTrue(map.ContainsKey(a, i));
      Assert.IsTrue(child.ContainsKey(i));

      map.Clear();
      Assert.IsFalse(map.ContainsKey(a, i));
      Assert.AreEqual(v, map[a, i]);

      Assert.IsTrue(map.ContainsKey(a, i));
      Assert.IsTrue(child.ContainsKey(i));
    }
  }
}
